using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using NppPluginNET.Speech;

namespace NppPluginNET
{
    partial class PluginBase
    {
        #region " Fields "
        internal const string PluginName = "NppSpeech";
        static string iniFilePath = null;
        static string sectionName = "Insert Extension";
        static string keyName = "doCloseTag";
        static string selectedLanguage = "";
        static bool doCloseTag = false;
        //static frmGoToLine frmGoToLine = null; //form specific
        //static internal int idFrmGotToLine = -1; //form specific
        //static Icon tbIcon = null; //form specific
        static Bitmap tbBmp = Properties.Resources.mic;
        static Bitmap tbBmp_tbTab = Properties.Resources.mic_bmp;

        static SpeechManager speechManager = new SpeechManager();

        static bool isActivated = false;

        #endregion

        #region " Startup/CleanUp "
        static internal void CommandMenuInit()
        {
            // Initialization of plugin commands
	        // get the parameters from the plugin config file (if any)
	        // get path of plugin configuration
            StringBuilder sbIniFilePath = new StringBuilder(Win32.MAX_PATH);
            Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_GETPLUGINSCONFIGDIR, Win32.MAX_PATH, sbIniFilePath);
            iniFilePath = sbIniFilePath.ToString();

	        // if config path doesn't exist, create it
            if (!Directory.Exists(iniFilePath))
	        {
                Directory.CreateDirectory(iniFilePath);
	        }

	        // make your plugin config file full file path name
            iniFilePath = Path.Combine(iniFilePath, PluginName + ".ini");

	        // get the parameter value from plugin config
	        doCloseTag = (Win32.GetPrivateProfileInt(sectionName, keyName, 0, iniFilePath) != 0);

            // with function :
            // SetCommand(int index,                            // zero based number to indicate the order of command
            //            string commandName,                   // the command name that you want to see in plugin menu
            //            NppFuncItemDelegate functionPointer,  // the symbol of function (function pointer) associated with this command. The body should be defined below. See Step 4.
            //            ShortcutKey *shortcut,                // optional. Define a shortcut to trigger this command
            //            bool check0nInit                      // optional. Make this menu item be checked visually
            //            );
            SetCommand(0, "Activate Speech Control", activateSpeech);
            SetCommand(1, "---", null);
            SetCommand(2, "Speak Document", speak);
            SetCommand(3, "---", null);          
            SetCommand(4, "About", about);
            //SetCommand(4, "---", null);
            //SetCommand(5, "TestOutput", helloFX);
            //SetCommand(6, "TestOutput", WhatIsNpp);
            //SetCommand(3, "Configuration", DockableDlgDemo); idFrmGotToLine = 5;
            
        }

        internal static void UpdateLanguage()
        {
            // can't get lang name -> use langtype
            LangType langType = LangType.L_EXTERNAL;
            Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_GETCURRENTLANGTYPE, 0, ref langType);
            speechManager.LanguageType = langType;

        }

        static internal void SetToolBarIcon()
        {
            toolbarIcons tbIcons = new toolbarIcons();
            tbIcons.hToolbarBmp = tbBmp.GetHbitmap();
            IntPtr pTbIcons = Marshal.AllocHGlobal(Marshal.SizeOf(tbIcons));
            Marshal.StructureToPtr(tbIcons, pTbIcons, false);
            Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_ADDTOOLBARICON, _funcItems.Items[0]._cmdID, pTbIcons); //index 0 is the command initializeSpeech
            Marshal.FreeHGlobal(pTbIcons);
        }

        static internal void PluginCleanUp()
        {
	        Win32.WritePrivateProfileString(sectionName, keyName, doCloseTag ? "1" : "0", iniFilePath);
        }
        #endregion 

        #region " Speech functions "


        static void activateSpeech()
        {
            if(isActivated)
            {
                Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_SETMENUITEMCHECK, _funcItems.Items[0]._cmdID, 0);
                disposeSpeech();
                isActivated = false;

            }
            else
            {
                Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_SETMENUITEMCHECK, _funcItems.Items[0]._cmdID, 1);
                initializeSpeech();
                isActivated = true;
            }

        }
 
        //initializes the speech manager and registers event handlers
        static void initializeSpeech()
        {
            Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_SETMENUITEMCHECK, _funcItems.Items[0]._cmdID, 1);
            speechManager.Initialize();
            //register eventhandlers
            speechManager.CodeGenerationRecognized += CodeGenerationRecognized;
            speechManager.EditorNavigationRecognized += EditorNavigationRecognized;
            speechManager.TextManipulationRecognized += TextManipulationRecognized;
            UpdateLanguage();
        }

        //stop speech recognition
        static void disposeSpeech()
        {
            Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_SETMENUITEMCHECK, _funcItems.Items[0]._cmdID, 0);
            speechManager.Dispose();
        }

        static string AddIdentationToText(string text)
        {
          int currentPos = (int)Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_GETCURRENTPOS, 0, 0);
          int currentLine = (int)Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_LINEFROMPOSITION, currentPos, 0);
          int lineStartPos = (int)Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_POSITIONFROMLINE, currentLine, 0);
          int identationCnt = currentPos - lineStartPos;
       
          string identation = "";
          for (int i = 0; i < identationCnt; i++)
            identation += " ";
          return text.Replace("\\n","\\n" + identation);
        }

        //handle code generation
        static void CodeGenerationRecognized(object sender, CodeGenerationEventArgs e)
        {
            int currentPos = (int)Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_GETCURRENTPOS, 0, 0);
            string text = Regex.Unescape(AddIdentationToText(e.ParseResult.Text));
            Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_INSERTTEXT, -1, text);
            Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_SETCURRENTPOS, (currentPos + e.ParseResult.CursorPosition), 0);
        }

        //handle editor navigation
        static void EditorNavigationRecognized(object sender, EditorControlEventArgs e)
        {

            //http://www.scintilla.org/ScintillaDoc.html#SCI_GETANCHOR -> scintilla commands
            // http://sourceforge.net/apps/mediawiki/notepad-plus/index.php?title=Messages_And_Notifications -> npp commands
            if (EditorControl.GoTo.Equals(e.EditorControl))
            {
                // TODO : Line number is always 0
                int number = e.Number;
                if (ControlScope.Line.Equals(e.ControlScope))
                {
                    //goto line
                    IntPtr curScintilla = PluginBase.GetCurrentScintilla();
                    Win32.SendMessage(curScintilla, SciMsg.SCI_ENSUREVISIBLE, number - 1, 0);
                    Win32.SendMessage(curScintilla, SciMsg.SCI_GOTOLINE, number - 1, 0);
                    Win32.SendMessage(curScintilla, SciMsg.SCI_GRABFOCUS, 0, 0);
                }
                else //tab
                {
                    //goto tab
                    // TODO: Could not find command -> Remove go to tab
                }
            }
            else if (EditorControl.New.Equals(e.EditorControl))
            {
                //Tab or File
                //open new tab
                Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_FILE_NEW);
            }
            else if (EditorControl.Save.Equals(e.EditorControl)) 
            {
                if (ControlScope.All.Equals(e.ControlScope))
                {
                    //save all
                    Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_FILE_SAVEALL);
                }
                else if (ControlScope.As.Equals(e.ControlScope))
                {
                    //save as
                    Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_FILE_SAVEAS);
                }
                else // File or None
                {
                    //save file
                    Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_FILE_SAVE);
                }
            }
            else if (EditorControl.Close.Equals(e.EditorControl)) 
            {
                if (ControlScope.Tab.Equals(e.ControlScope))
                {
                    //close tab
                    Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_FILE_CLOSE);
                }
                else // Tab or None
                {
                    //close notepad
                    Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_FILE_EXIT);
                }
            }
            else if (EditorControl.Find.Equals(e.EditorControl))
            {
                //open search dialog of notepad
                Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_SEARCH_FIND);
            }
            else if (EditorControl.Replace.Equals(e.EditorControl))
            {
                //open replace dialog of notepad
                Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_SEARCH_REPLACE);
            }
            else if (EditorControl.Undo.Equals(e.EditorControl))
            {
                //Undo
                Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_EDIT_UNDO);
            }
            else if (EditorControl.Redo.Equals(e.EditorControl))
            {
                //Redo
                Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_EDIT_REDO);
            }
        }

        //handle texte manipulation
        static void TextManipulationRecognized(object sender, EditorControlEventArgs e)
        {
            IntPtr curScintilla = PluginBase.GetCurrentScintilla();
            if (EditorControl.Select.Equals(e.EditorControl))
            {
                if (ControlScope.All.Equals(e.ControlScope))
                {
                    //select whole document
                    Win32.SendMessage(curScintilla, SciMsg.SCI_SELECTALL, 0, 0);
                }
                else // Line
                {
                    //select line
                    int currentPos = (int)Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_GETCURRENTPOS, 0, 0);
                    int currentLine = (int)Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_LINEFROMPOSITION, currentPos, 0);
                    int lineStartPos = (int)Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_POSITIONFROMLINE, currentLine, 0);
                    int lineEndPos = (int)Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_GETLINEENDPOSITION, currentLine, 0);
                    Win32.SendMessage(curScintilla, SciMsg.SCI_SETSELECTION, lineStartPos, lineEndPos);
                }
            }
            else if (EditorControl.Delete.Equals(e.EditorControl))
            {
                if (ControlScope.All.Equals(e.ControlScope))
                {
                    //delete all
                    Win32.SendMessage(curScintilla, SciMsg.SCI_CLEARALL, 0, 0);
                }
                else // Line
                {
                    //delete line
                    Win32.SendMessage(curScintilla, SciMsg.SCI_LINEDELETE, 0, 0);
                }
            }
            else if (EditorControl.Copy.Equals(e.EditorControl))
            {      
                Win32.SendMessage(curScintilla, SciMsg.SCI_COPY, 0, 0);
                //strg+c
            }
            else if (EditorControl.Paste.Equals(e.EditorControl))
            {
                //strg+v
                Win32.SendMessage(curScintilla, SciMsg.SCI_PASTE, 0, 0);
            }
            else if (EditorControl.Cut.Equals(e.EditorControl))
            {
                //strg+x
                Win32.SendMessage(curScintilla, SciMsg.SCI_CUT, 0, 0);
            }
        }

        static void about()
        {
            MessageBox.Show("Master Software Engineering\n FH Hagenberg\n Mobile and ubiquitous Systems\n Esterer A. and Steiner P.");
        }

        static void speak()
        {
            string textToSpeak = NPPPlugin.GetCurrentDocumentText();
            speechManager.Speak(textToSpeak);
        }

        #endregion

        #region " functions for testing or not needed anymore "
        
        /*
        static void hello()
        {
            // Open a new document
            Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_FILE_NEW);
            // Say hello now :
            // Scintilla control has no Unicode mode, so we use ANSI here (marshalled as ANSI by default)
            Win32.SendMessage(GetCurrentScintilla(), SciMsg.SCI_SETTEXT, 0, "This is not Sparta - this is Patrick speaking!!!");
        }
        static void helloFX()
        {
            hello();
            new Thread(callbackHelloFX).Start();
        }
        static void callbackHelloFX()
        {
            IntPtr curScintilla = GetCurrentScintilla();
            int currentZoomLevel = (int)Win32.SendMessage(curScintilla, SciMsg.SCI_GETZOOM, 0, 0);
            int i = currentZoomLevel;
            for (int j = 0; j < 4; j++)
            {
                for (; i >= -10; i--)
                {
                    Win32.SendMessage(curScintilla, SciMsg.SCI_SETZOOM, i, 0);
                    Thread.Sleep(30);
                }
                Thread.Sleep(100);
                for (; i <= 20; i++)
                {
                    Thread.Sleep(30);
                    Win32.SendMessage(curScintilla, SciMsg.SCI_SETZOOM, i, 0);
                }
                Thread.Sleep(100);
            }
            for (; i >= currentZoomLevel; i--)
            {
                Thread.Sleep(30);
                Win32.SendMessage(curScintilla, SciMsg.SCI_SETZOOM, i, 0);
            }
        }
        static void WhatIsNpp()
        {
            string text2display = "Notepad++ is a free (as in \"free speech\" and also as in \"free beer\") " +
                "source code editor and Notepad replacement that supports several languages.\n" +
                "Running in the MS Windows environment, its use is governed by GPL License.\n\n" +
                "Based on a powerful editing component Scintilla, Notepad++ is written in C++ and " +
                "uses pure Win32 API and STL which ensures a higher execution speed and smaller program size.\n" +
                "By optimizing as many routines as possible without losing user friendliness, Notepad++ is trying " +
                "to reduce the world carbon dioxide emissions. When using less CPU power, the PC can throttle down " +
                "and reduce power consumption, resulting in a greener environment.";
            new Thread(new ParameterizedThreadStart(callbackWhatIsNpp)).Start(text2display);
        }
        static void callbackWhatIsNpp(object data)
        {
            string text2display = (string)data;
            // Open a new document
            Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_MENUCOMMAND, 0, NppMenuCmd.IDM_FILE_NEW);

            // Get the current scintilla
            IntPtr curScintilla = GetCurrentScintilla();

            Random srand = new Random(DateTime.Now.Millisecond);
            int rangeMin = 0;
            int rangeMax = 250;
            for (int i = 0; i < text2display.Length; i++)
            {
                StringBuilder charToShow = new StringBuilder(text2display[i].ToString());

                int ranNum = srand.Next(rangeMin, rangeMax);
                Thread.Sleep(ranNum + 30);

                Win32.SendMessage(curScintilla, SciMsg.SCI_APPENDTEXT, 1, charToShow);
                Win32.SendMessage(curScintilla, SciMsg.SCI_GOTOPOS, (int)Win32.SendMessage(curScintilla, SciMsg.SCI_GETLENGTH, 0, 0), 0);
            }
        }

        static void DockableDlgDemo()
        {
            // Dockable Dialog Demo
            // 
            // This demonstration shows you how to do a dockable dialog.
            // You can create your own non dockable dialog - in this case you don't nedd this demonstration.
            if (frmGoToLine == null)
            {
                frmGoToLine = new frmGoToLine();

                using (Bitmap newBmp = new Bitmap(16, 16))
                {
                    Graphics g = Graphics.FromImage(newBmp);
                    ColorMap[] colorMap = new ColorMap[1];
                    colorMap[0] = new ColorMap();
                    colorMap[0].OldColor = Color.Fuchsia;
                    colorMap[0].NewColor = Color.FromKnownColor(KnownColor.ButtonFace);
                    ImageAttributes attr = new ImageAttributes();
                    attr.SetRemapTable(colorMap);
                    g.DrawImage(tbBmp_tbTab, new Rectangle(0, 0, 16, 16), 0, 0, 16, 16, GraphicsUnit.Pixel, attr);
                    tbIcon = Icon.FromHandle(newBmp.GetHicon());
                }

                NppTbData _nppTbData = new NppTbData();
                _nppTbData.hClient = frmGoToLine.Handle;
                _nppTbData.pszName = "Go To Line #";
                // the dlgDlg should be the index of funcItem where the current function pointer is in
                // this case is 15.. so the initial value of funcItem[15]._cmdID - not the updated internal one !
                _nppTbData.dlgID = idFrmGotToLine;
                // define the default docking behaviour
                _nppTbData.uMask = NppTbMsg.DWS_DF_CONT_RIGHT | NppTbMsg.DWS_ICONTAB | NppTbMsg.DWS_ICONBAR;
                _nppTbData.hIconTab = (uint)tbIcon.Handle;
                _nppTbData.pszModuleName = PluginName;
                IntPtr _ptrNppTbData = Marshal.AllocHGlobal(Marshal.SizeOf(_nppTbData));
                Marshal.StructureToPtr(_nppTbData, _ptrNppTbData, false);

                Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_DMMREGASDCKDLG, 0, _ptrNppTbData);
                // Following message will toogle both menu item state and toolbar button
                Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_SETMENUITEMCHECK, _funcItems.Items[idFrmGotToLine]._cmdID, 1);
            }
            else
            {
                if (!frmGoToLine.Visible)
                {
                    Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_DMMSHOW, 0, frmGoToLine.Handle);
                    Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_SETMENUITEMCHECK, _funcItems.Items[idFrmGotToLine]._cmdID, 1);
                }
                else
                {
                    Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_DMMHIDE, 0, frmGoToLine.Handle);
                    Win32.SendMessage(nppData._nppHandle, NppMsg.NPPM_SETMENUITEMCHECK, _funcItems.Items[idFrmGotToLine]._cmdID, 0);
                }
            }
            frmGoToLine.textBox1.Focus();
        }*/
        #endregion
    }
}   
